package fr.upmc.dtgui.reflexion;

import java.lang.reflect.Modifier;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtField;
import javassist.CtMethod;
import fr.upmc.dtgui.annotations.IntegerRange;
import fr.upmc.dtgui.annotations.RealRange;
import fr.upmc.dtgui.annotations.VariationType;

/**
 * <b>PanelFactory est la classe chargée de générer les différents panels qui
 * vont venir composer l'interface de la station de téléopération.</b>
 * <p>
 * Cette classe permet de créer les différents panels de contrôle et de
 * surveillance via les méthodes:
 * <ul>
 * <li>makeSteeringPanel, qui va générer (si les annotations le demandent) le
 * panel de lecture et d'écriture du steering</li>
 * <li>makeEnergyPanel, qui va générer (si les annotations le demandent) le
 * panel de lecture de l'energie</li>
 * <li>makeSpeedPanel, qui va générer (si les annotations le demandent) le panel
 * de lecture et d'écriture de la vitesse</li>
 * </ul>
 * </p>
 * 
 * @see MakeTranslator
 * @see TeleoperationBoardFactory
 * @see AnnotationParser
 * @see javax.swing.JPanel
 * 
 * @author Ayoub Benali, Maxime Sarilar
 */

public class PanelFactory {

	/**
	 * La méthode makeSteeringPanel.
	 * <p>
	 * Cette méthode va créer les deux panels relatifs au steering angle.
	 * </p>
	 * 
	 * @param board
	 *            La CtClass de la station de téléopération.
	 * @param pool
	 *            Le ClassPool associé à la classe chargée.
	 * @param ct
	 *            La CtClass du robot.
	 * @param hasRSteering
	 *            Le booléen vrai si le robot permet aux stations de lire son
	 *            steering.
	 * @param hasWSteering
	 *            Le booléen vrai si le robot permet aux stations d'agir sur son
	 *            steering.
	 * @param rSteering
	 *            L'ensemble borné des valeurs que peut prendre le steering sous
	 *            la forme de réels (vaut nul si le steering est un entier ou un
	 *            booléen).
	 * @param iSteering
	 *            L'ensemble borné des valeurs que peut prendre le steering sous
	 *            la forme d'entiers (vaut nul si le steering est un réel ou un
	 *            booléen).
	 * @param vSteering
	 *            Le type de variation que prendra la valeur du steering.
	 * @return La CtClass héritant de JPanel et étant le panel relatif au
	 *         steering.
	 */
	public static CtClass makeSteeringPanel(CtClass board, ClassPool pool,
			CtClass ct, boolean hasRSteering, boolean hasWSteering,
			RealRange rSteering, IntegerRange iSteering, VariationType vSteering
			, String steeringRUnit, String steeringWUnit)
			throws Exception {
		CtClass steeringPanel = board.makeNestedClass("SteeringPanel", true);
		steeringPanel.setSuperclass(pool.getCtClass("javax.swing.JPanel"));

		/*
		 * On va rajouter du code spécifique à la classe si l'AnnotationParser a
		 * trouvé les bonnes annotations à la classe du robot.
		 */
		String steeringDisplayPanelCode = "", SteeringControllerPanelCode = "";
		if (hasRSteering) {
			CtClass steeringDisplayPanel = makeSteeringDisplayPanel(pool,
					board, rSteering, iSteering, vSteering, ct, steeringRUnit);
			CtField sdp = new CtField(steeringDisplayPanel, "sdp",
					steeringPanel);
			steeringPanel.addField(sdp);
			steeringDisplayPanelCode = "$0.sdp = new "
					+ steeringDisplayPanel.getName() + "() ;";
		}
		if (hasWSteering) {
			CtClass steeringControllerPanel = makeSteeringControllerPanel(pool,
					board, ct, rSteering, iSteering, vSteering, steeringWUnit);
			CtField scp = new CtField(steeringControllerPanel, "scp",
					steeringPanel);
			steeringPanel.addField(scp);
			SteeringControllerPanelCode = "$0.scp = new "
					+ steeringControllerPanel.getName() + "() ;";
		}

		// On rajoute le field suivant, qui permettra de lire les infos du
		// robot:
		CtField lr = new CtField(
				pool.getCtClass("fr.upmc.dtgui.robot.InstrumentedRobot"), "lr",
				steeringPanel);
		steeringPanel.addField(lr);

		/*
		 * Le constructeur va dépendre des deux chaînes de caractère fixées plus
		 * plus haut. En effet, on insère ces chaînes dans le corps du
		 * constructeur lui-même:
		 */
		CtConstructor steeringPanelConstructor = new CtConstructor(
				new CtClass[] {}, steeringPanel);
		String steeringPanelConstructorBody = "{"
				+ "$0.setLayout(new java.awt.BorderLayout()) ;"
				+ "$0.setSize(450, 250) ;" + steeringDisplayPanelCode
				+ SteeringControllerPanelCode
				+ "$0.add(sdp, java.awt.BorderLayout.NORTH) ;"
				+ "$0.add(scp, java.awt.BorderLayout.SOUTH) ;"
				+ "$0.setVisible(true) ;" + "}";
		steeringPanelConstructor.setBody(steeringPanelConstructorBody);
		steeringPanel.addConstructor(steeringPanelConstructor);

		// Finalement il reste à rajouter les méthodes nécessaires:
		CtMethod disconnectRobot = new CtMethod(CtClass.voidType,
				"disconnectRobot",
				new CtClass[] { pool
						.getCtClass("fr.upmc.dtgui.robot.InstrumentedRobot") },
				steeringPanel);
		CtMethod connectRobot = new CtMethod(CtClass.voidType, "connectRobot",
				new CtClass[] { pool
						.getCtClass("fr.upmc.dtgui.robot.InstrumentedRobot") },
				steeringPanel);
		CtMethod updateSteeringAngle = new CtMethod(CtClass.voidType,
				"updateSteeringAngle",
				new CtClass[] { pool.get("java.lang.Object") }, steeringPanel);

		String disconnectRobotBody = 
				"{$0.scp.disconnectRobot($1) ; $0.lr = null;}";
		String connectRobotBody = "{$0.lr = $1 ; $0.scp.connectRobot($1) ;}";
		String updateSteeringAngleBody = "{$0.sdp.updateSteeringAngle($1) ;}";

		disconnectRobot.setBody(disconnectRobotBody);
		connectRobot.setBody(connectRobotBody);
		updateSteeringAngle.setBody(updateSteeringAngleBody);

		disconnectRobot.setModifiers(Modifier.PUBLIC);
		connectRobot.setModifiers(Modifier.PUBLIC);
		updateSteeringAngle.setModifiers(Modifier.PUBLIC);

		steeringPanel.addMethod(disconnectRobot);
		steeringPanel.addMethod(connectRobot);
		steeringPanel.addMethod(updateSteeringAngle);

		/*
		 * On finalise la classe pour garantir qu'elle ne sera pas modifiée
		 * passé cette méthode:
		 */
		steeringPanel.toClass();
		return steeringPanel;
	}

	/**
	 * La méthode makeSteeringControllerPanel.
	 * <p>
	 * Cette méthode va créer le panel de contrôle du steering du robot.
	 * </p>
	 * 
	 * @param pool
	 *            Le ClassPool associé à la classe chargée.
	 * @param board
	 *            La CtClass de la station de téléopération.
	 * @param ct
	 *            La CtClass du robot.
	 * @param rSteering
	 *            L'ensemble borné des valeurs que peut prendre le steering sous
	 *            la forme de réels (vaut nul si le steering est un entier ou un
	 *            booléen).
	 * @param iSteering
	 *            L'ensemble borné des valeurs que peut prendre le steering sous
	 *            la forme d'entiers (vaut nul si le steering est un réel ou un
	 *            booléen).
	 * @param vSteering
	 *            Le type de variation que prendra la valeur du steering.
	 * @param steeringWUnit
	 *            L'unité pour l'écriture du steering.
	 * @return La CtClass héritant de JPanel et étant le panel relatif au
	 *         steering.
	 */
	public static CtClass makeSteeringControllerPanel(ClassPool pool,
			CtClass board, CtClass ct, RealRange rSteering,
			IntegerRange iSteering, VariationType vSteering, 
			String steeringWUnit) throws Exception {
		CtClass steeringController = board.makeNestedClass(
				"SteeringControllerPanel", true);

		steeringController.setSuperclass(pool.getCtClass("javax.swing.JPanel"));

		// Les champs suivant font partie intégrante du panel:
		CtField lr = new CtField(
				pool.getCtClass("fr.upmc.dtgui.robot.InstrumentedRobot"), "lr",
				steeringController);
		CtField steeringLabelPanel = new CtField(
				pool.getCtClass("javax.swing.JPanel"), "steeringLabelPanel",
				steeringController);
		CtField steeringSliderPanel = new CtField(
				pool.getCtClass("javax.swing.JPanel"), "steeringSliderPanel",
				steeringController);
		CtField steeringSlider = new CtField(
				pool.getCtClass("javax.swing.JSlider"), "steeringSlider",
				steeringController);

		steeringController.addField(lr);
		steeringController.addField(steeringLabelPanel);
		steeringController.addField(steeringSliderPanel);
		steeringController.addField(steeringSlider);

		/*
		 * On va maintenant sauvegarder la portée du JSlider selon les
		 * paramètres de la méthode rSteering et iSteering:
		 */
		String maxRange, minRange;
		if (rSteering != null) {
			maxRange = String.valueOf(rSteering.sup());
			minRange = String.valueOf(rSteering.inf());
		} else if (iSteering != null) {
			maxRange = String.valueOf(iSteering.sup());
			minRange = String.valueOf(iSteering.inf());
		} else // Dans ce cas, le steering se mesure en booléen:
		{
			maxRange = "1";
			minRange = "0";
		}

		// On va crée le DataListener du panel:
		CtClass sadl = makeSteeringActuatorDataListener(board, pool, ct);

		/*
		 * Le corps du constructeur va être construit selon les paramètres fixés
		 * lors de l'appel de la méthode:
		 */
		CtConstructor steeringControllerConstructor = new CtConstructor(
				new CtClass[] {}, steeringController);
		String steeringControllerCosntructorBody = "{"
				+ "$0.setLayout(new java.awt.BorderLayout()) ;"
				+ "$0.setSize(450, 125) ;"
				+ "javax.swing.JLabel steeringLabel = " 
				+ "new javax.swing.JLabel(\"Steering angle control ("
				+ steeringWUnit + ")\");"
				+ "steeringLabelPanel = new javax.swing.JPanel() ;"
				+ "steeringLabelPanel.add(steeringLabel) ;"
				+ "$0.add(steeringLabelPanel, java.awt.BorderLayout.SOUTH) ;"
				+ "javax.swing.DefaultBoundedRangeModel steeringModel ="
				+ "new javax.swing.DefaultBoundedRangeModel(0, 0, (int)"
				+ minRange
				+ ", (int)"
				+ maxRange
				+ ") ;"
				+ "steeringSlider = new javax.swing.JSlider(steeringModel) ;"
				+ "steeringSlider.setMajorTickSpacing(5);"
				+ "steeringSlider.setMinorTickSpacing(1);"
				+ "steeringSlider.setPaintTicks(true);"
				+ "steeringSlider.setPaintLabels(true);"
				+ "steeringSlider.setOrientation(javax.swing.JSlider."
				+ (vSteering.name().equals("RANDOM") ? "HORIZONTAL"
						: "VERTICAL")
				+ ") ;"
				+ "steeringSliderPanel = new javax.swing.JPanel() ;"
				+ "steeringSliderPanel.add(steeringSlider) ;"
				+ "$0.add(steeringSliderPanel, java.awt.BorderLayout.NORTH) ;"
				+ "$0.setBorder(javax.swing.BorderFactory.createLineBorder(" 
				+ "java.awt.Color.BLACK, 4)) ;"
				+ "$0.setVisible(true) ;" + "}";
		steeringControllerConstructor
				.setBody(steeringControllerCosntructorBody);
		steeringController.addConstructor(steeringControllerConstructor);

		// On doit rajouter les deux méthodes suivantes pour interfacer le
		// robot:
		CtMethod disconnectRobot = new CtMethod(CtClass.voidType,
				"disconnectRobot",
				new CtClass[] { pool
						.getCtClass("fr.upmc.dtgui.robot.InstrumentedRobot") },
				steeringController);
		CtMethod connectRobot = new CtMethod(CtClass.voidType, "connectRobot",
				new CtClass[] { pool
						.getCtClass("fr.upmc.dtgui.robot.InstrumentedRobot") },
				steeringController);

		String disconnectRobotBody = 
				"{$0.steeringSlider.addChangeListener(null) ; $0.lr = null ;}";
		String connectRobotBody = 
				"{$0.lr = $1 ; $0.steeringSlider.addChangeListener( new "
				+ sadl.getName() + "($1.getActuatorDataQueue(), lr)) ;}";

		disconnectRobot.setBody(disconnectRobotBody);
		connectRobot.setBody(connectRobotBody);

		steeringController.addMethod(connectRobot);
		steeringController.addMethod(disconnectRobot);

		/*
		 * On finalise la classe pour garantir qu'elle ne sera pas modifiée
		 * passé cette méthode:
		 */
		steeringController.toClass();
		return steeringController;
	}

	/**
	 * La méthode makeEnergyPanel.
	 * <p>
	 * Cette méthode va créer le panel de lecture d'énergie du robot.
	 * </p>
	 * 
	 * @param pool
	 *            Le ClassPool associé à la classe chargée.
	 * @param board
	 *            La CtClass de la station de téléopération.
	 * @param rEnergy
	 *            L'ensemble borné des valeurs que peut prendre l'énergie sous
	 *            la forme de réels (vaut nul si l'énergie est un entier ou un
	 *            booléen).
	 * @param iEnergy
	 *            L'ensemble borné des valeurs que peut prendre l'énergie sous
	 *            la forme d'entiers (vaut nul si l'énergie est un réel ou un
	 *            booléen).
	 * @param vEnergy
	 *            Le type de variation que prendra la valeur de l'énergie.
	 * @param energyRUnit
	 *            L'unité pour la lecture du niveau d'énergie.
	 * @return La CtClass héritant de JPanel et étant le panel relatif au
	 *         steering.
	 */
	public static CtClass makeEnergyPanel(CtClass board, ClassPool pool,
			RealRange rEnergy, IntegerRange iEnergy, VariationType vEnergy,
			String energyRUnit) throws Exception {
		CtClass ep = board.makeNestedClass("EnergyPanel", true);
		ep.setSuperclass(pool.getCtClass("javax.swing.JPanel"));

		/*
		 * On va maintenant sauvegarder la portée du JSlider selon les
		 * paramètres de la méthode rSteering et iSteering:
		 */
		String maxRange = "0", minRange = "0";
		if (iEnergy != null) {
			maxRange = String.valueOf(iEnergy.sup());
			minRange = String.valueOf(iEnergy.inf());
		} else if (rEnergy != null) {
			maxRange = String.valueOf(rEnergy.sup());
			minRange = String.valueOf(rEnergy.inf());
		} else // Dans ce cas, le steering se mesure en booléen:
		{
			maxRange = "1";
			minRange = "0";
		}

		// Les champs suivant font partie intégrante du panel:
		CtField energyModel = new CtField(
				pool.getCtClass("javax.swing.BoundedRangeModel"),
				"energyModel", ep);
		CtField jpEnergySlider = new CtField(
				pool.getCtClass("javax.swing.JPanel"), "jpEnergySlider", ep);
		CtField jpEcvLabel = new CtField(pool.getCtClass("javax.swing.JPanel"),
				"jpEcvLabel", ep);

		ep.addField(energyModel);
		ep.addField(jpEnergySlider);
		ep.addField(jpEcvLabel);

		/*
		 * Le corps du constructeur va être construit selon les paramètres fixés
		 * lors de l'appel de la méthode:
		 */
		CtConstructor epConstructor = new CtConstructor(new CtClass[] {}, ep);
		String epConstructorBody = "{"
				+ "$0.setSize(50, 250) ;"
				+ "$0.setLayout(" 
				+ "new javax.swing.BoxLayout($0,javax.swing.BoxLayout.Y_AXIS));"
				+ "$0.energyModel = " 
				+ "new javax.swing.DefaultBoundedRangeModel(0, 0, (int)"
				+ minRange
				+ ", (int)"
				+ maxRange
				+ ") ;"
				+ "javax.swing.JSlider energySlider = " 
				+ "new javax.swing.JSlider(energyModel) ;"
				+ "energySlider.setOrientation(javax.swing.JSlider."
				+ (vEnergy.name().equals("RANDOM") ? "HORIZONTAL" : "VERTICAL")
				+ ") ;"
				+ "energySlider.setMajorTickSpacing(20);"
				+ "energySlider.setMinorTickSpacing(5);"
				+ "energySlider.setPaintTicks(true);"
				+ "energySlider.setPaintLabels(true);"
				+ "jpEnergySlider = new javax.swing.JPanel() ;"
				+ "jpEnergySlider.add(energySlider) ;"
				+ "$0.add(jpEnergySlider) ;"
				+ "javax.swing.JLabel ecvLabel =" 
				+ "new javax.swing.JLabel(\"Remaining energy in "
				+ energyRUnit + "\") ;"
				+ "jpEcvLabel = new javax.swing.JPanel() ;"
				+ "jpEcvLabel.setLayout(new java.awt.BorderLayout()) ;"
				+ "jpEcvLabel.add(ecvLabel, java.awt.BorderLayout.NORTH) ;"
				+ "$0.add(jpEcvLabel) ;"
				+ "$0.setBorder(javax.swing.BorderFactory.createLineBorder(" 
				+ "java.awt.Color.BLACK, 4)) ;"
				+ "$0.setVisible(true) ;" + "}";
		epConstructor.setBody(epConstructorBody);
		ep.addConstructor(epConstructor);

		// On doit rajouter les deux méthodes suivantes pour interfacer le
		// robot:
		CtMethod setVisible = new CtMethod(CtClass.voidType, "setVisible",
				new CtClass[] { CtClass.booleanType }, ep);
		String setVisibleBody = "{" + "super.setVisible($1);"
				+ "$0.jpEnergySlider.setVisible($1) ;"
				+ "$0.jpEcvLabel.setVisible($1) ;" + "}";
		setVisible.setBody(setVisibleBody);
		ep.addMethod(setVisible);

		CtMethod updateEnergy = new CtMethod(CtClass.voidType, "updateEnergy",
				new CtClass[] { pool.getCtClass("java.lang.Object") }, ep);
		String updateEnergyBody = "{"
				+ "try {	"
				+ "$0.energyModel.setValue((int) " 
				+ "Math.round($1.getClass().getField(\"level\")." 
				+ "getDouble($1)));"
				+ "} catch (Exception e) {" + "e.printStackTrace();" + "}"
				+ "}";
		updateEnergy.setBody(updateEnergyBody);
		ep.addMethod(updateEnergy);

		/*
		 * On finalise la classe pour garantir qu'elle ne sera pas modifiée
		 * passé cette méthode:
		 */
		ep.toClass();
		return ep;
	}

	/**
	 * La méthode makeSpeedPanel.
	 * <p>
	 * Cette méthode va créer les deux panels relatifs à la vitesse.
	 * </p>
	 * 
	 * @param pool
	 *            Le ClassPool associé à la classe chargée.
	 * @param board
	 *            La CtClass de station de téléopération.
	 * @param ct
	 *            La CtClass du robot.
	 * @param hasRSpeed
	 *            Le booléen vrai si le robot permet aux stations de lire sa
	 *            vitesse.
	 * @param hasWSpeed
	 *            Le booléen vrai si le robot permet aux stations d'agir sur sa
	 *            vitesse.
	 * @param rSpeed
	 *            L'ensemble borné des valeurs que peut prendre la vitesse sous
	 *            la forme de réels (vaut nul si la vitesse est un entier ou un
	 *            booléen).
	 * @param iSpeed
	 *            L'ensemble borné des valeurs que peut prendre la vitesse sous
	 *            la forme d'entiers (vaut nul si la vitesse est un réel ou un
	 *            booléen).
	 * @param vSpeed
	 *            Le type de variation que prendra la valeur de la vitesse.
	 * @return La CtClass héritant de JPanel et étant le panel relatif à la
	 *         vitesse.
	 */
	public static CtClass makeSpeedPanel(CtClass board, ClassPool pool,
			CtClass ct, boolean hasRSpeed, boolean hasWSpeed, RealRange rSpeed,
			IntegerRange iSpeed, VariationType vSpeed, String speedRUnit,
			String speedWUnit) throws Exception {
		CtClass speedPanel = board.makeNestedClass("speedPanel", true);
		speedPanel.setSuperclass(pool.getCtClass("javax.swing.JPanel"));

		/*
		 * On va rajouter du code spécifique à la classe si l'AnnotationParser a
		 * trouvé les bonnes annotations à la classe du robot.
		 */
		String speedDisplayPanelCode = "", SpeedControllerPanelCode = "";
		if (hasWSpeed) {
			CtClass controller = makeSpeedControllerPanel(board, pool, ct,
					rSpeed, iSpeed, vSpeed, speedWUnit);
			CtField scp = new CtField(controller, "scp", speedPanel);
			scp.setModifiers(Modifier.PROTECTED);
			speedPanel.addField(scp);
			SpeedControllerPanelCode = "$0.scp = new " + controller.getName()
					+ "() ;";
		}
		if (hasRSpeed) {
			CtClass display = makeSpeedDisplayPanel(board, pool, rSpeed,
					iSpeed, vSpeed, speedRUnit);
			CtField sdp = new CtField(display, "sdp", speedPanel);
			sdp.setModifiers(Modifier.PROTECTED);
			speedPanel.addField(sdp);
			speedDisplayPanelCode = "$0.sdp = new " + display.getName()
					+ "() ;";
		}

		// On rajoute le field suivant, qui permettra de lire les infos du
		// robot:
		CtField lr = new CtField(
				pool.getCtClass("fr.upmc.dtgui.robot.InstrumentedRobot"), "lr",
				speedPanel);
		lr.setModifiers(Modifier.PROTECTED);
		speedPanel.addField(lr);

		/*
		 * Le constructeur va dépendre des deux chaînes de caractère fixées plus
		 * plus haut. En effet, on insère ces chaînes dans le corps du
		 * constructeur lui-même:
		 */
		CtConstructor speedPanelConstructor = new CtConstructor(
				new CtClass[] {}, speedPanel);
		String speedPanelConstructorBody = "{"
				+ "$0.setLayout(new java.awt.BorderLayout()) ;"
				+ "$0.setSize(450, 250) ;" + speedDisplayPanelCode
				+ SpeedControllerPanelCode
				+ "$0.add(sdp, java.awt.BorderLayout.NORTH) ;"
				+ "$0.add(scp, java.awt.BorderLayout.SOUTH) ;"
				+ "$0.setVisible(true) ;" + "}";
		speedPanelConstructor.setBody(speedPanelConstructorBody);
		speedPanelConstructor.setModifiers(Modifier.PUBLIC);
		speedPanel.addConstructor(speedPanelConstructor);

		// Finalement il reste à rajouter les méthodes nécessaires:
		CtMethod disconnectRobot = new CtMethod(CtClass.voidType,
				"disconnectRobot",
				new CtClass[] { pool
						.getCtClass("fr.upmc.dtgui.robot.InstrumentedRobot") },
				speedPanel);
		disconnectRobot
				.setBody("{$0.scp.disconnectRobot($1) ; $0.lr = null ;}");
		disconnectRobot.setModifiers(Modifier.PUBLIC);
		speedPanel.addMethod(disconnectRobot);

		CtMethod connectRobot = new CtMethod(CtClass.voidType, "connectRobot",
				new CtClass[] { pool
						.getCtClass("fr.upmc.dtgui.robot.InstrumentedRobot") },
				speedPanel);
		connectRobot.setBody("{$0.lr = $1 ; " + "$0.scp.connectRobot($1) ;}");
		connectRobot.setModifiers(Modifier.PUBLIC);
		speedPanel.addMethod(connectRobot);

		CtMethod updateSpeed = new CtMethod(CtClass.voidType, "updateSpeed",
				new CtClass[] { pool.getCtClass("java.lang.Object") },
				speedPanel);
		updateSpeed.setBody("{$0.sdp.updateSpeed($1) ;}");
		updateSpeed.setModifiers(Modifier.PUBLIC);
		speedPanel.addMethod(updateSpeed);

		/*
		 * On finalise la classe pour garantir qu'elle ne sera pas modifiée
		 * passé cette méthode:
		 */
		speedPanel.toClass();
		return speedPanel;
	}

	/**
	 * La méthode makeSpeedDisplayPanel.
	 * <p>
	 * Cette méthode va créer le panel de lecture de la vitesse du robot.
	 * </p>
	 * 
	 * @param pool
	 *            Le ClassPool associé à la classe chargée.
	 * @param board
	 *            La CtClass de la station de téléopération.
	 * @param rSpeed
	 *            L'ensemble borné des valeurs que peut prendre la vitesse sous
	 *            la forme de réels (vaut nul si la vitesse est un entier ou un
	 *            booléen).
	 * @param iSpeed
	 *            L'ensemble borné des valeurs que peut prendre la vitesse sous
	 *            la forme d'entiers (vaut nul si la vitesse est un réel ou un
	 *            booléen).
	 * @param vSpeed
	 *            Le type de variation que prendra la valeur de la vitesse.
	 * @param speedRUnit
	 *            L'unité pour la lecture de la vitesse.
	 * @return La CtClass héritant de JPanel et étant le panel relatif à la
	 *         vitesse.
	 */
	public static CtClass makeSpeedDisplayPanel(CtClass board, ClassPool pool,
			RealRange rSpeed, IntegerRange iSpeed, VariationType vSpeed,
			String speedRUnit) throws Exception {
		CtClass speedDisplayPanel = board.makeNestedClass("speedDisplayPanel",
				true);
		speedDisplayPanel.setSuperclass(pool.getCtClass("javax.swing.JPanel"));

		// Les champs suivant font partie intégrante du panel:
		CtField speedModel = new CtField(
				pool.getCtClass("javax.swing.BoundedRangeModel"), "speedModel",
				speedDisplayPanel);
		speedModel.setModifiers(Modifier.PROTECTED);
		speedDisplayPanel.addField(speedModel);

		CtField jpProgressBar = new CtField(
				pool.getCtClass("javax.swing.JPanel"), "jpProgressBar",
				speedDisplayPanel);
		jpProgressBar.setModifiers(Modifier.PROTECTED);
		speedDisplayPanel.addField(jpProgressBar);

		CtField speedLabelPanel = new CtField(
				pool.getCtClass("javax.swing.JPanel"), "speedLabelPanel",
				speedDisplayPanel);
		speedLabelPanel.setModifiers(Modifier.PROTECTED);
		speedDisplayPanel.addField(speedLabelPanel);

		/*
		 * On va maintenant sauvegarder la portée du JSlider selon les
		 * paramètres de la méthode rSpeed et iSpeed:
		 */
		String maxRange, minRange;
		if (rSpeed != null) {
			maxRange = String.valueOf(rSpeed.sup());
			minRange = String.valueOf(rSpeed.inf());
		} else if (iSpeed != null) {
			maxRange = String.valueOf(iSpeed.sup());
			minRange = String.valueOf(iSpeed.inf());
		} else // Dans ce cas, la vitesse se mesure en booléen:
		{
			maxRange = "1";
			minRange = "0";
		}

		/*
		 * Le corps du constructeur va être construit selon les paramètres fixés
		 * lors de l'appel de la méthode:
		 */
		CtConstructor speedDisplayPanelConstructor = new CtConstructor(
				new CtClass[] {}, speedDisplayPanel);
		speedDisplayPanelConstructor.setModifiers(Modifier.PUBLIC);
		String speedDisplayPanelConstructorBody = "{"
				+ "$0.setLayout(new java.awt.BorderLayout()) ;"
				+ "$0.setSize(450, 125) ;"
				+ "jpProgressBar = new javax.swing.JPanel() ;"
				+ "jpProgressBar.setLayout(new java.awt.FlowLayout()) ;"
				+ "$0.speedModel = " 
				+ "new javax.swing.DefaultBoundedRangeModel(0, 0, (int)"
				+ minRange
				+ ", (int)"
				+ maxRange
				+ ") ;"
				+ "javax.swing.JSlider speedSlider = " 
				+ "new javax.swing.JSlider(speedModel) ;"
				+ "speedSlider.setMajorTickSpacing(5);"
				+ "speedSlider.setMinorTickSpacing(1);"
				+ "speedSlider.setPaintTicks(true);"
				+ "speedSlider.setPaintLabels(true);"
				+ "speedSlider.setOrientation(javax.swing.JSlider."
				+ (vSpeed.name().equals("RANDOM") ? "HORIZONTAL" : "VERTICAL")
				+ ") ;"
				+ "jpProgressBar.add(speedSlider) ;"
				+ "$0.add(jpProgressBar, java.awt.BorderLayout.NORTH) ;"
				+ "javax.swing.JLabel speedLabel = " 
				+ "new javax.swing.JLabel(\"Current speed ("+speedRUnit+")\") ;"
				+ "speedLabelPanel = new javax.swing.JPanel() ;"
				+ "speedLabelPanel.add(speedLabel) ;"
				+ "$0.add(speedLabelPanel, java.awt.BorderLayout.SOUTH) ;"
				+ "$0.setBorder(javax.swing.BorderFactory.createLineBorder(" 
				+ "java.awt.Color.BLACK, 4)) ;"
				+ "$0.setVisible(true) ;" + "}";
		speedDisplayPanelConstructor.setBody(speedDisplayPanelConstructorBody);
		speedDisplayPanel.addConstructor(speedDisplayPanelConstructor);

		// On aura besoin de cette méthode pour mettre à jour la valeur
		// affichée:
		CtMethod updateSpeed = new CtMethod(CtClass.voidType, "updateSpeed",
				new CtClass[] { pool.getCtClass("java.lang.Object") },
				speedDisplayPanel);
		updateSpeed.setModifiers(Modifier.PUBLIC);
		String updateSpeedBody = "{"
				+ "try {"
				+ " $0.speedModel.setValue((int) " 
				+"Math.round($1.getClass().getField(\"speed\").getDouble($1)));"
				+ "}" + "catch(Exception e) {" + " e.printStackTrace();" + "}"
				+ "}";
		updateSpeed.setBody(updateSpeedBody);
		speedDisplayPanel.addMethod(updateSpeed);

		/*
		 * On finalise la classe pour garantir qu'elle ne sera pas modifiée
		 * passé cette méthode:
		 */
		speedDisplayPanel.toClass();
		return speedDisplayPanel;
	}

	/**
	 * La méthode makeSpeedControllerPanel.
	 * <p>
	 * Cette méthode va créer le panel de contrôle de la vitesse du robot.
	 * </p>
	 * 
	 * @param pool
	 *            Le ClassPool associé à la classe chargée.
	 * @param board
	 *            La CtClass de la station de téléopération.
	 * @param ct
	 *            La CtClass du robot.
	 * @param rSpeed
	 *            L'ensemble borné des valeurs que peut prendre la vitesse sous
	 *            la forme de réels (vaut nul si la vitesse est un entier ou un
	 *            booléen).
	 * @param iSpeed
	 *            L'ensemble borné des valeurs que peut prendre la vitesse sous
	 *            la forme d'entiers (vaut nul si la vitesse est un réel ou un
	 *            booléen).
	 * @param vSpeed
	 *            Le type de variation que prendra la valeur de la vitesse.
	 * @param speedWUnit
	 *            L'unité pour l'écriture de la vitesse.
	 * @return La CtClass héritant de JPanel et étant le panel relatif à la
	 *         vitesse.
	 */
	public static CtClass makeSpeedControllerPanel(CtClass board,
			ClassPool pool, CtClass ct, RealRange rSpeed, IntegerRange iSpeed,
			VariationType vSpeed, String speedWUnit) throws Exception {
		CtClass speedControllerPanel = board.makeNestedClass(
				"speedControllerPanel", true);
		speedControllerPanel.setSuperclass(pool
				.getCtClass("javax.swing.JPanel"));

		// On va crée le DataListener du panel:
		CtClass sadl = makeSpeedActuatorDataListener(board, pool, ct);

		// Les champs suivant font partie intégrante du panel:
		CtField serialVersionUID = new CtField(CtClass.longType,
				"serialVersionUID", speedControllerPanel);
		serialVersionUID.setModifiers(Modifier.PRIVATE | Modifier.STATIC
				| Modifier.FINAL);
		speedControllerPanel.addField(serialVersionUID);

		CtField lr = new CtField(
				pool.getCtClass("fr.upmc.dtgui.robot.InstrumentedRobot"), "lr",
				speedControllerPanel);
		lr.setModifiers(Modifier.PROTECTED);
		speedControllerPanel.addField(lr);

		CtField speedLabelPanel = new CtField(
				pool.getCtClass("javax.swing.JPanel"), "speedLabelPanel",
				speedControllerPanel);
		speedLabelPanel.setModifiers(Modifier.PROTECTED);
		speedControllerPanel.addField(speedLabelPanel);

		CtField speedSliderPanel = new CtField(
				pool.getCtClass("javax.swing.JPanel"), "speedSliderPanel",
				speedControllerPanel);
		speedSliderPanel.setModifiers(Modifier.PROTECTED);
		speedControllerPanel.addField(speedSliderPanel);

		CtField jpProgressBarPanel = new CtField(
				pool.getCtClass("javax.swing.JPanel"), "jpProgressBar",
				speedControllerPanel);
		speedLabelPanel.setModifiers(Modifier.PROTECTED);
		speedControllerPanel.addField(jpProgressBarPanel);

		CtField speedSlider = new CtField(
				pool.getCtClass("javax.swing.JSlider"), "speedSlider",
				speedControllerPanel);
		speedSlider.setModifiers(Modifier.PROTECTED);
		speedControllerPanel.addField(speedSlider);

		/*
		 * On va maintenant sauvegarder la portée du JSlider selon les
		 * paramètres de la méthode rSpeed et iSpeed:
		 */
		String maxRange, minRange;
		if (rSpeed != null) {
			maxRange = String.valueOf(rSpeed.sup());
			minRange = String.valueOf(rSpeed.inf());
		} else if (iSpeed != null) {
			maxRange = String.valueOf(iSpeed.sup());
			minRange = String.valueOf(iSpeed.inf());
		} else // Dans ce cas, la vitesse se mesure en booléen:
		{
			maxRange = "1";
			minRange = "0";
		}

		/*
		 * Le corps du constructeur va être construit selon les paramètres fixés
		 * lors de l'appel de la méthode:
		 */
		CtConstructor speedControllerPanelConstructor = new CtConstructor(
				new CtClass[] {}, speedControllerPanel);
		speedControllerPanelConstructor.setModifiers(Modifier.PUBLIC);
		String speedControllerPanelConstructorBody = "{"
				+ "$0.setLayout(new java.awt.BorderLayout()) ;"
				+ "$0.setSize(450, 125) ;"
				+ "javax.swing.JLabel speedLabel = " 
				+ "new  javax.swing.JLabel(\"Speed control ("+speedWUnit+")\");"
				+ "speedLabelPanel = new  javax.swing.JPanel() ;"
				+ "speedLabelPanel.add(speedLabel) ;"
				+ "$0.add(speedLabelPanel, java.awt.BorderLayout.SOUTH) ;"
				+ "javax.swing.DefaultBoundedRangeModel speedModel ="
				+ "new  javax.swing.DefaultBoundedRangeModel(0, 0, (int)"
				+ minRange
				+ ", (int)"
				+ maxRange
				+ ") ;"
				+ "speedSlider = new javax.swing.JSlider(speedModel) ;"
				+ "speedSlider.setMajorTickSpacing(5);"
				+ "speedSlider.setMinorTickSpacing(1);"
				+ "speedSlider.setPaintTicks(true);"
				+ "speedSlider.setPaintLabels(true);"
				+ "speedSlider.setOrientation(javax.swing.JSlider."
				+ (vSpeed.name().equals("RANDOM") ? "HORIZONTAL" : "VERTICAL")
				+ ") ;"
				+ "speedSliderPanel = new  javax.swing.JPanel() ;"
				+ "speedSliderPanel.add(speedSlider) ;"
				+ "$0.add(speedSliderPanel, java.awt.BorderLayout.NORTH) ;"
				+ "$0.setBorder(javax.swing.BorderFactory.createLineBorder(" 
				+ "java.awt.Color.BLACK, 4)) ;"
				+ "$0.setVisible(true) ;" + "}";
		speedControllerPanelConstructor
				.setBody(speedControllerPanelConstructorBody);
		speedControllerPanel.addConstructor(speedControllerPanelConstructor);

		// On doit rajouter les deux méthodes suivantes pour interfacer le
		// robot:
		CtMethod disconnectRobot = new CtMethod(CtClass.voidType,
				"disconnectRobot",
				new CtClass[] { pool
						.getCtClass("fr.upmc.dtgui.robot.InstrumentedRobot") },
				speedControllerPanel);
		disconnectRobot.setModifiers(Modifier.PUBLIC);
		disconnectRobot
			   .setBody("{$0.speedSlider.addChangeListener(null);$0.lr=null;}");
		speedControllerPanel.addMethod(disconnectRobot);

		CtMethod connectRobot = new CtMethod(CtClass.voidType, "connectRobot",
				new CtClass[] { pool
						.getCtClass("fr.upmc.dtgui.robot.InstrumentedRobot") },
				speedControllerPanel);
		connectRobot.setModifiers(Modifier.PUBLIC | Modifier.SYNCHRONIZED);
		connectRobot.setBody("{$0.lr = $1 ; "
				+ "$0.speedSlider.addChangeListener(new " + sadl.getName()
				+ "(lr.getActuatorDataQueue(),lr));}");
		speedControllerPanel.addMethod(connectRobot);

		/*
		 * On finalise la classe pour garantir qu'elle ne sera pas modifiée
		 * passé cette méthode:
		 */
		speedControllerPanel.toClass();
		return speedControllerPanel;
	}

	/**
	 * La méthode makeSteeringDisplayPanel.
	 * <p>
	 * Cette méthode va créer le panel de lecture du steering du robot.
	 * </p>
	 * 
	 * @param pool
	 *            Le ClassPool associé à la classe chargée.
	 * @param board
	 *            La CtClass de la station de téléopération.
	 * @param ct
	 *            La CtClass du robot.
	 * @param rSteering
	 *            L'ensemble borné des valeurs que peut prendre le steering sous
	 *            la forme de réels (vaut nul si le steering est un entier ou un
	 *            booléen).
	 * @param iSteering
	 *            L'ensemble borné des valeurs que peut prendre le steering sous
	 *            la forme d'entiers (vaut nul si le steering est un réel ou un
	 *            booléen).
	 * @param vSteering
	 *            Le type de variation que prendra la valeur du steering.
	 * @param steeringRUnit
	 *            L'unité pour la lecture du steering.
	 * @return La CtClass héritant de JPanel et étant le panel relatif à la
	 *         lecture du steering.
	 */
	public static CtClass makeSteeringDisplayPanel(ClassPool pool,
			CtClass board, RealRange rSteering, IntegerRange iSteering,
			VariationType vSteering, CtClass ct, String steeringRUnit) 
					throws Exception {
		CtClass steeringDisplay = board.makeNestedClass("SteeringDisplayPanel",
				true);

		steeringDisplay.setSuperclass(pool.getCtClass("javax.swing.JPanel"));

		// Les champs suivant font partie intégrante du panel:
		CtField steeringModel = new CtField(
				pool.getCtClass("javax.swing.BoundedRangeModel"),
				"steeringModel", steeringDisplay);
		CtField jpProgressBar = new CtField(
				pool.getCtClass("javax.swing.JPanel"), "jpProgressBar",
				steeringDisplay);
		CtField speedLabelPanel = new CtField(
				pool.getCtClass("javax.swing.JPanel"), "steeringLabelPanel",
				steeringDisplay);

		steeringDisplay.addField(steeringModel);
		steeringDisplay.addField(jpProgressBar);
		steeringDisplay.addField(speedLabelPanel);

		/*
		 * On va maintenant sauvegarder la portée du JSlider selon les
		 * paramètres de la méthode rSpeed et iSpeed:
		 */
		String maxRange, minRange;
		if (rSteering != null) {
			maxRange = String.valueOf(rSteering.sup());
			minRange = String.valueOf(rSteering.inf());
		} else if (iSteering != null) {
			maxRange = String.valueOf(iSteering.sup());
			minRange = String.valueOf(iSteering.inf());
		} else // Dans ce cas, la vitesse se mesure en booléen:
		{
			maxRange = "1";
			minRange = "0";
		}

		/*
		 * Le corps du constructeur va être construit selon les paramètres fixés
		 * lors de l'appel de la méthode:
		 */
		CtConstructor steeringDisplayConstructor = new CtConstructor(
				new CtClass[] {}, steeringDisplay);
		String steeringDisplayConstructorBody = "{"
				+ "$0.setLayout(new java.awt.BorderLayout()) ;"
				+ "$0.setSize(450, 125) ;"
				+ "jpProgressBar = new javax.swing.JPanel() ;"
				+ "jpProgressBar.setLayout(new java.awt.FlowLayout()) ;"
				+ "$0.steeringModel = " 
				+ "new javax.swing.DefaultBoundedRangeModel(0, 0,(int)"
				+ minRange
				+ ", (int)"
				+ maxRange
				+ ") ;"
				+ "javax.swing.JSlider steeringSlider = " 
				+ "new javax.swing.JSlider($0.steeringModel) ;"
				+ "steeringSlider.setMajorTickSpacing(5);"
				+ "steeringSlider.setMinorTickSpacing(1);"
				+ "steeringSlider.setPaintTicks(true);"
				+ "steeringSlider.setPaintLabels(true);"
				+ "jpProgressBar.add(steeringSlider) ;"
				+ "steeringSlider.setOrientation(javax.swing.JSlider."
				+ (vSteering.name().equals("RANDOM") ? "HORIZONTAL"
						: "VERTICAL")
				+ ") ;"
				+ "$0.add(jpProgressBar, java.awt.BorderLayout.NORTH) ;"
				+ "javax.swing.JLabel steeringLabel = " 
				+ "new javax.swing.JLabel(\"Current steering angle("
				+ steeringRUnit + ")\");"
				+ "steeringLabelPanel = new javax.swing.JPanel() ;"
				+ "steeringLabelPanel.add(steeringLabel) ;"
				+ "$0.add(steeringLabelPanel, java.awt.BorderLayout.SOUTH) ;"
				+ "$0.setBorder(javax.swing.BorderFactory.createLineBorder" 
				+"(java.awt.Color.BLACK, 4)) ;"
				+ "$0.setVisible(true) ;" + "}";
		steeringDisplayConstructor.setBody(steeringDisplayConstructorBody);
		steeringDisplay.addConstructor(steeringDisplayConstructor);

		// On aura besoin de cette méthode pour mettre à jour la valeur
		// affichée:
		CtMethod updateSteeringAngle = new CtMethod(CtClass.voidType,
				"updateSteeringAngle",
				new CtClass[] { pool.getCtClass("java.lang.Object") },
				steeringDisplay);
		updateSteeringAngle.setModifiers(Modifier.PUBLIC);
		String updateSteeringAngleBody = "{try {" + ct.getName()
				+ "$SteeringData sd = (" + ct.getName() + "$SteeringData) $1;"
				+ "double toto = sd.getSteeringAngle();"
				+ "$0.steeringModel.setValue((int) toto) ;" + "}"
				+ "catch(Exception e) {" + "	e.printStackTrace();" + "}}";
		updateSteeringAngle.setBody(updateSteeringAngleBody);
		steeringDisplay.addMethod(updateSteeringAngle);

		/*
		 * On finalise la classe pour garantir qu'elle ne sera pas modifiée
		 * passé cette méthode:
		 */
		steeringDisplay.toClass();
		return steeringDisplay;
	}

	/**
	 * La méthode makeSteeringActuatorDataListener.
	 * <p>
	 * Cette méthode va créer le data listener permettant d'agir sur le robot
	 * lorsque l'utilisateur changera la position du JSlider de contrôle de
	 * steering.
	 * </p>
	 * 
	 * @param pool
	 *            Le ClassPool associé à la classe chargée.
	 * @param board
	 *            La CtClass de la station de téléopération.
	 * @param ct
	 *            La CtClass du robot.
	 * @return Le data listener.
	 */
	public static CtClass makeSteeringActuatorDataListener(CtClass board,
			ClassPool pool, CtClass ct) throws Exception {
		CtClass sadl = board.makeNestedClass("SteeringActuatorDataListener",
				true);
		sadl.addInterface(pool.getCtClass("javax.swing.event.ChangeListener"));

		// On aura besoin de ces deux champs pour mettre à jour le robot:
		CtField commandQueue = new CtField(
				pool.getCtClass("java.util.concurrent.BlockingQueue"),
				"commandQueue", sadl);
		sadl.addField(commandQueue);
		CtField lr = new CtField(
				pool.getCtClass("fr.upmc.dtgui.robot.InstrumentedRobot"), "lr",
				sadl);
		sadl.addField(lr);

		// Le constructeur du datalistener:
		CtConstructor sadlConstructor = new CtConstructor(new CtClass[] {
				pool.getCtClass("java.util.concurrent.BlockingQueue"),
				pool.getCtClass("fr.upmc.dtgui.robot.InstrumentedRobot") },
				sadl);
		String sadlConstructorBody = "{" + "super();" + "$0.commandQueue = $1;"
				+ "$0.lr = $2;" + "}";
		sadlConstructor.setBody(sadlConstructorBody);
		sadl.addConstructor(sadlConstructor);

		// La méthode qui sera affectée au listener du JSlider de contrôle:
		CtMethod stateChanged = new CtMethod(CtClass.voidType, "stateChanged",
				new CtClass[] { pool
						.getCtClass("javax.swing.event.ChangeEvent") }, sadl);
		String stateChangedBody = "{"
				+ "javax.swing.JSlider source = $1.getSource() ;"
				+ "double newSteeringAngle = source.getValue() ;"
				+ "try {"
				+ "	final fr.upmc.dtgui.robot.RobotActuatorCommand sc = "
				+ ct.getName()
				+ ".makeSteeringChange(newSteeringAngle);"
				+ "	(new " + board.getSimpleName()
				+ "$ActuatorDataSender(sc, $0.commandQueue)).start() ;" + "}"
				+ "catch(Exception ex) {" + "	ex.printStackTrace();" + "}"
				+ "}";
		stateChanged.setBody(stateChangedBody);
		sadl.addMethod(stateChanged);

		/*
		 * On finalise la classe pour garantir qu'elle ne sera pas modifiée
		 * passé cette méthode:
		 */
		sadl.toClass();
		return sadl;
	}

	/**
	 * La méthode makeSpeedActuatorDataListener.
	 * <p>
	 * Cette méthode va créer le data listener permettant d'agir sur le robot
	 * lorsque l'utilisateur changera la position du JSlider de contrôle de
	 * vitesse.
	 * </p>
	 * 
	 * @param pool
	 *            Le ClassPool associé à la classe chargée.
	 * @param board
	 *            La CtClass de la station de téléopération.
	 * @param ct
	 *            La CtClass du robot.
	 * @return Le data listener
	 */
	public static CtClass makeSpeedActuatorDataListener(CtClass board,
			ClassPool pool, CtClass ct) throws Exception {
		CtClass speedActuatorDataListener = board.makeNestedClass(
				"SpeedActuatorDataListener", true);
		speedActuatorDataListener.addInterface(pool
				.getCtClass("javax.swing.event.ChangeListener"));

		// On aura besoin de ces deux champs pour mettre à jour le robot:
		CtField commandQueue = new CtField(
				pool.getCtClass("java.util.concurrent.BlockingQueue"),
				"commandQueue", speedActuatorDataListener);
		commandQueue.setModifiers(Modifier.FINAL | Modifier.PROTECTED);
		speedActuatorDataListener.addField(commandQueue);
		CtField lr = new CtField(
				pool.getCtClass("fr.upmc.dtgui.robot.InstrumentedRobot"), "lr",
				speedActuatorDataListener);
		speedActuatorDataListener.addField(lr);

		// Le constructeur du datalistener:
		CtConstructor speedActuatorDataListenerConstructor = new CtConstructor(
				new CtClass[] {
						pool.getCtClass("java.util.concurrent.BlockingQueue"),
					  pool.getCtClass("fr.upmc.dtgui.robot.InstrumentedRobot")},
				speedActuatorDataListener);
		speedActuatorDataListenerConstructor
				.setBody("{$0.commandQueue = $1;$0.lr=$2;}");
		speedActuatorDataListener
				.addConstructor(speedActuatorDataListenerConstructor);

		// La méthode qui sera affectée au listener du JSlider de contrôle:
		CtMethod stateChanged = new CtMethod(CtClass.voidType, "stateChanged",
				new CtClass[] { pool
						.getCtClass("javax.swing.event.ChangeEvent") },
				speedActuatorDataListener);
		stateChanged.setModifiers(Modifier.PUBLIC);
		String stateChangedBody = "{"
				+ "javax.swing.JSlider source = $1.getSource() ;"
				+ "double newSpeed = source.getValue() ;" + "try {"
				+ "final fr.upmc.dtgui.robot.RobotActuatorCommand sc ="
				+ ct.getName() + ".makeSpeedChange(newSpeed);" + " (new "
				+ board.getSimpleName()
				+ "$ActuatorDataSender(sc, $0.commandQueue)).start() ;" + "}"
				+ "catch(Exception ex) {" + " ex.printStackTrace();" + "}"
				+ "}";
		stateChanged.setBody(stateChangedBody);
		speedActuatorDataListener.addMethod(stateChanged);

		/*
		 * On finalise la classe pour garantir qu'elle ne sera pas modifiée
		 * passé cette méthode:
		 */
		speedActuatorDataListener.toClass();
		return speedActuatorDataListener;

	}
}
